// AgentProxy.cpp

#include "StdAfx.h"

#include "Common/MS/PropVariant.h"
#include "Common/String/DataConversions.h"

#include "Common/Archive/OpenArchive.h"

#include "AgentProxy.h"

namespace Common
{
namespace Agent
{

int CProxyFolder::FindDirSubItemIndex(const CString &name, int &insertPos) const
{
  size_t left = 0, right = Folders.size();
  for (;;)
  {
    if (left == right)
    {
      insertPos = left;
      return -1;
    }
    int mid = (left + right) / 2;
    int compare = name.CompareNoCase(Folders[mid]->Name);
    if (compare == 0)
      return mid;
    if (compare < 0)
      right = mid;
    else
      left = mid + 1;
  }
}

int CProxyFolder::FindDirSubItemIndex(const CString &name) const
{
  int insertPos;
  return FindDirSubItemIndex(name, insertPos);
}

void CProxyFolder::AddFileSubItem(UINT32 index, const CString &name)
{
	Files.push_back(CProxyFile());
	Files.back().Name = name;
	Files.back().Index = index;
}

std::shared_ptr<CProxyFolder> CProxyFolder::AddDirSubItem(UINT32 index, bool leaf, const CString &name)
{
  int insertPos;
  int folderIndex = FindDirSubItemIndex(name, insertPos);
  if (folderIndex >= 0)
  {
    auto item = Folders[folderIndex];
    if (leaf)
    {
      item->Index = index;
      item->IsLeaf = true;
    }
    return item;
  }
  std::shared_ptr<CProxyFolder> item = std::make_shared<CProxyFolder>();
  Folders.insert(Folders.begin() + insertPos, item);
  //CProxyFolder *item = &Folders[insertPos];
  item->Name = name;
  item->Index = index;
  item->Parent = this;
  item->IsLeaf = leaf;
  return item;
}

void CProxyFolder::Clear()
{
  Folders.clear();
  Files.clear();
}

void CProxyFolder::GetPathParts(CStringVector &pathParts) const
{
  pathParts.clear();
  const CProxyFolder *current = this;
  while (current->Parent != NULL)
  {
	  pathParts.push_front((const wchar_t *)current->Name);
    current = current->Parent;
  }
}

CString CProxyFolder::GetFullPathPrefix() const
{
  CString result;
  const CProxyFolder *current = this;
  while (current->Parent != NULL)
  {
    result = current->Name + CString(CHAR_PATH_SEPARATOR) + result;
    current = current->Parent;
  }
  return result;
}

CString CProxyFolder::GetItemName(UINT32 index) const
{
  if (index < (UINT32)Folders.size())
    return Folders[index]->Name;
  return Files[index - Folders.size()].Name;
}

void CProxyFolder::AddRealIndices(std::vector<UINT32> &realIndices) const
{
  if (IsLeaf)
    realIndices.push_back(Index);
  size_t i;
  for (i = 0; i < Folders.size(); i++)
    Folders[i]->AddRealIndices(realIndices);
  realIndices.reserve(Files.size());
  for (i = 0; i < Files.size(); i++)
    realIndices.push_back(Files[i].Index);
}

void CProxyFolder::GetRealIndices(const UINT32 *indices, UINT32 numItems, std::vector<UINT32> &realIndices) const
{
  realIndices.clear();
  realIndices.reserve(numItems);
  for (UINT32 i = 0; i < numItems; i++)
  {
    int index = indices[i];
    int numDirItems = Folders.size();
    if (index < numDirItems)
      Folders[index]->AddRealIndices(realIndices);
    else
		realIndices.push_back(Files[index - numDirItems].Index);
  }
  std::sort(realIndices.begin(), realIndices.end());
}

///////////////////////////////////////////////
// CProxyArchive

static UINT64 GetSize(IInArchive *archive, UINT32 index, PROPID propID)
{
  CComPropVariant prop;
  if (archive->GetProperty(index, propID, &prop) == S_OK)
    if (prop.vt != VT_EMPTY)
      return String::ConvertPropVariantToUInt64(prop);
  return 0;
}

void CProxyFolder::CalculateSizes(IInArchive *archive)
{
  Size = PackSize = 0;
  NumSubFolders = Folders.size();
  NumSubFiles = Files.size();
  CrcIsDefined = true;
  Crc = 0;
  size_t i;
  for (i = 0; i < Files.size(); i++)
  {
    UINT32 index = Files[i].Index;
    Size += GetSize(archive, index, kpidSize);
    PackSize += GetSize(archive, index, kpidPackSize);
    {
      CComPropVariant prop;
      if (archive->GetProperty(index, kpidCRC, &prop) == S_OK && prop.vt == VT_UI4)
        Crc += prop.ulVal;
      else
        CrcIsDefined = false;
    }
  }
  for (i = 0; i < Folders.size(); i++)
  {
    auto &f = Folders[i];
    f->CalculateSizes(archive);
    Size += f->Size;
    PackSize += f->PackSize;
    NumSubFiles += f->NumSubFiles;
    NumSubFolders += f->NumSubFolders;
    Crc += f->Crc;
    if (!f->CrcIsDefined)
      CrcIsDefined = false;
  }
}

HRESULT CProxyArchive::Load(const Common::Archive::CArc * const pArc, IProgress *progress)
{
	ATLASSERT(NULL != pArc);
  RootFolder->Clear();
  IInArchive *archive = pArc->Archive;
  HRESULT hRes(S_OK);
  {
    ThereIsPathProp = false;
    UINT32 numProps;
    archive->GetNumberOfProperties(&numProps);
    for (UINT32 i = 0; i < numProps; i++)
    {
      CComBSTR name;
      PROPID propID;
      VARTYPE varType;
      hRes = archive->GetPropertyInfo(i, &name, &propID, &varType);
	  if(S_OK != hRes)
		return hRes;
      if (propID == kpidPath)
      {
        ThereIsPathProp = true;
        break;
      }
    }
  }

  UINT32 numItems;
  hRes = archive->GetNumberOfItems(&numItems);
  if(S_OK != hRes)
	return hRes;
  if (progress != NULL)
  {
    UINT64 totalItems = numItems;
    hRes = progress->SetTotal(totalItems);
	if(S_OK != hRes)
		return hRes;
  }
  CString fileName;
  for (UINT32 i = 0; i < numItems; i++)
  {
    if (progress != NULL && (i & 0xFFFFF) == 0)
    {
      UINT64 currentItemIndex = i;
      hRes = progress->SetCompleted(&currentItemIndex);
	  if(S_OK != hRes)
		return hRes;
    }
    CString filePath;
    hRes = pArc->GetItemPath(i, filePath);
	if(S_OK != hRes)
		return hRes;
    auto curItem = RootFolder;
    int len = filePath.GetLength();
    fileName.Empty();
    for (int j = 0; j < len; j++)
    {
      wchar_t c = filePath[j];
      if (c == CHAR_PATH_SEPARATOR || c == _T('/'))
      {
        curItem = curItem->AddDirSubItem((UINT32)(INT32)-1, false, fileName);
        fileName.Empty();
      }
      else
        fileName += c;
    }

    bool isFolder;
    hRes = Common::Archive::IsArchiveItemFolder(archive, i, isFolder);
	if(S_OK != hRes)
		return hRes;
    if (isFolder)
      curItem->AddDirSubItem(i, true, fileName);
    else
      curItem->AddFileSubItem(i, fileName);
  }
  RootFolder->CalculateSizes(archive);
  return S_OK;
}

}}